package at.technikum_wien.embsys.aat.PriscillaCore.component;

import java.util.Iterator;
import java.util.List;

import at.technikum_wien.embsys.aat.PriscillaCore.enums.BinaryState;
import at.technikum_wien.embsys.aat.PriscillaCore.enums.EnoceanComponentType;
import at.technikum_wien.embsys.aat.PriscillaCore.enums.WindowHandleState;
import at.technikum_wien.embsys.aat.PriscillaCore.event.FrameEventBinary;
import at.technikum_wien.embsys.aat.PriscillaCore.event.FrameEventWindowHandle;
import at.technikum_wien.embsys.aat.PriscillaCore.link.event.LinkListener;
import at.technikum_wien.embsys.aat.PriscillaCore.telegram.Telegram;

/**
 * 
 * EEP_05_xx_xx component class
 * 
 * Types: switches Functions: 2 or 4 buttons
 * 
 * @author R. Wagner
 * @author B. Aigner
 * 
 */
public class EEP_05_xx_xx implements EnoceanComponent {

	Telegram telegram; // enocean telegram
	List<?> eventListener; // Event listener for firing events
	BinaryState contactA = BinaryState.UNKNOWN; // Contact A state
	BinaryState contactB = BinaryState.UNKNOWN; // Contact B state
	BinaryState contactC = BinaryState.UNKNOWN; // Contact C state
	BinaryState contactD = BinaryState.UNKNOWN; // Contact D state
	BinaryState contact[] = new BinaryState[4]; // Contact state array
	WindowHandleState windowHandle = WindowHandleState.UNKNOWN; // WindowHandleState

	EnoceanComponentType enoceanComponentType = EnoceanComponentType.EEP_05_xx_xx; // declare
																					// component
																					// typ
	boolean telegramCreation = false; // true if this is a new created telegram
	int nrContactsChanged = 0; // Number of changed contacts
	int size = 4; // Number of contacts

	/**
	 * This constructor is used for received packets. It will parse the telegram
	 * and set the corresponding members.
	 * 
	 * @param telegram
	 *            Received Telegram
	 * @param listener
	 *            Event subscriber list
	 */
	public EEP_05_xx_xx(Telegram telegram, List<?> listener) {
		super();
		this.telegram = telegram;
		this.eventListener = listener;
		for (int i = 0; i < size; i++) {
			contact[i] = BinaryState.UNKNOWN; // init all contacts as unknown
		}
		windowHandle = WindowHandleState.UNKNOWN;	// init

		String binaryStringData3 = String.format("%8s", Integer.toBinaryString(telegram.getData3() & 0xFF)).replace(' ', '0');
//		System.out.println("binaryStringData3   " + binaryStringData3);

		if (binaryStringData3.equals("11110000")) {
			windowHandle = WindowHandleState.DOWN;
		} else if (binaryStringData3.equals("11010000")) {
			windowHandle = WindowHandleState.UP;
		} else if (binaryStringData3.equals("11000000")) {
			windowHandle = WindowHandleState.UP_2_MIDDLE;
		} else if (binaryStringData3.equals("11100000")) {
			windowHandle = WindowHandleState.DOWN_2_MIDDLE;
		}

		// check for EB-Bit
		if (checkBit(telegram.getData3(), 4)) {
			// check for type field
			if (telegram.getType() == 1) {
				// check every data bit and set the corresponding contact state
				if ((((byte) telegram.getData3() & 0xE0) >> 5) == 0) {
					contactA = BinaryState.FALSE;
					contact[0] = BinaryState.FALSE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 1) {
					contactA = BinaryState.TRUE;
					contact[0] = BinaryState.TRUE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 2) {
					contactB = BinaryState.FALSE;
					contact[1] = BinaryState.FALSE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 3) {
					contactB = BinaryState.TRUE;
					contact[1] = BinaryState.TRUE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 4) {
					contactC = BinaryState.FALSE;
					contact[2] = BinaryState.FALSE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 5) {
					contactC = BinaryState.TRUE;
					contact[2] = BinaryState.TRUE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 6) {
					contactD = BinaryState.FALSE;
					contact[3] = BinaryState.FALSE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 7) {
					contactD = BinaryState.TRUE;
					contact[3] = BinaryState.TRUE;
					nrContactsChanged++;
				}
				// second action valid?
				if (checkBit(telegram.getData3(), 0)) {
					// check every data bit and set the corresponding contact
					// state
					if ((((byte) telegram.getData3() & 0x0E) >> 1) == 0) {
						contactA = BinaryState.FALSE;
						contact[0] = BinaryState.FALSE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 1) {
						contactA = BinaryState.TRUE;
						contact[0] = BinaryState.TRUE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 2) {
						contactB = BinaryState.FALSE;
						contact[1] = BinaryState.FALSE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 3) {
						contactB = BinaryState.TRUE;
						contact[1] = BinaryState.TRUE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 4) {
						contactC = BinaryState.FALSE;
						contact[2] = BinaryState.FALSE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 5) {
						contactC = BinaryState.TRUE;
						contact[2] = BinaryState.TRUE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 6) {
						contactD = BinaryState.FALSE;
						contact[3] = BinaryState.FALSE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 7) {
						contactD = BinaryState.TRUE;
						contact[3] = BinaryState.TRUE;
						nrContactsChanged++;
					}
				}

			} else if (telegram.getType() == 2) {
				if ((((byte) telegram.getData3() & 0xE0) >> 5) == 0) {
					contactA = BinaryState.TRUE;
					contact[0] = BinaryState.TRUE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 1) {
					contactA = BinaryState.FALSE;
					contact[0] = BinaryState.FALSE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 2) {
					contactB = BinaryState.TRUE;
					contact[1] = BinaryState.TRUE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 3) {
					contactB = BinaryState.FALSE;
					contact[1] = BinaryState.FALSE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 4) {
					contactC = BinaryState.TRUE;
					contact[2] = BinaryState.TRUE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 5) {
					contactC = BinaryState.FALSE;
					contact[2] = BinaryState.FALSE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 6) {
					contactD = BinaryState.TRUE;
					contact[3] = BinaryState.TRUE;
					nrContactsChanged++;
				} else if ((((byte) telegram.getData3() & 0xE0) >> 5) == 7) {
					contactD = BinaryState.FALSE;
					contact[3] = BinaryState.FALSE;
					nrContactsChanged++;
				}
				if (checkBit(telegram.getData3(), 0)) {
					if ((((byte) telegram.getData3() & 0x0E) >> 1) == 0) {
						contactA = BinaryState.TRUE;
						contact[0] = BinaryState.TRUE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 1) {
						contactA = BinaryState.FALSE;
						contact[0] = BinaryState.FALSE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 2) {
						contactB = BinaryState.TRUE;
						contact[1] = BinaryState.TRUE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 3) {
						contactB = BinaryState.FALSE;
						contact[1] = BinaryState.FALSE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 4) {
						contactC = BinaryState.TRUE;
						contact[2] = BinaryState.TRUE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 5) {
						contactC = BinaryState.FALSE;
						contact[2] = BinaryState.FALSE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 6) {
						contactD = BinaryState.TRUE;
						contact[3] = BinaryState.TRUE;
						nrContactsChanged++;
					} else if ((((byte) telegram.getData3() & 0x0E) >> 1) == 7) {
						contactD = BinaryState.FALSE;
						contact[3] = BinaryState.FALSE;
						nrContactsChanged++;
					}
				}
			}
		}
		fireFrame();
	}

	/**
	 * Fire all associated events
	 */
	void fireFrame() {
		if (windowHandle != WindowHandleState.UNKNOWN) {
			Iterator<?> listen = eventListener.iterator();
			final FrameEventWindowHandle frameEventWindowHandle = new FrameEventWindowHandle(this, telegram.getDeviceID(), windowHandle);
			while (listen.hasNext()) {
				((LinkListener) listen.next()).frameReceived_WindowHandle(frameEventWindowHandle);
			}
		} else {
			Iterator<?> listen = eventListener.iterator();
			final FrameEventBinary frameEventBinary = new FrameEventBinary(this, telegram.getDeviceID(), contactA, contactB, contactC, contactD);
			while (listen.hasNext()) {
				((LinkListener) listen.next()).frameReceived_Binary(frameEventBinary);
			}
		}
	}

	/**
	 * Void constructor. It is used to create a new telegram for sending
	 */
	public EEP_05_xx_xx() {
		super();
		telegram = new Telegram();
		telegramCreation = true;
	}

	/**
	 * This constructor is used to create a telegram (the device id is given as
	 * parameter)
	 * 
	 * @param deviceID
	 *            Full device ID
	 */
	public EEP_05_xx_xx(String deviceID) {
		super();
		telegram = new Telegram();
		telegram.setDeviceID(deviceID);
		telegramCreation = true;
	}

	boolean checkBit(int value, int bitNr) {
		return (value & (1 << bitNr)) != 0;
	}

	int setBit(int value, int bitNr) {
		return value | (1 << bitNr);
	}

	int clrBit(int value, int bitNr) {
		return value & (~(1 << bitNr));
	}

	@Override
	public String getDeviceID() {
		return telegram.getDeviceID();
	}

	@Override
	public String getORG() {
		return telegram.getxORG();
	}

	@Override
	public EnoceanComponentType getType() {
		return enoceanComponentType;
	}

	@Override
	public void setDeviceID(String deviceID) {
		telegram.setDeviceID(deviceID);
	}

	/**
	 * Creates a new telegram from the component
	 * 
	 * This method creates a new telegram (if the component is initialized with
	 * a constructor without a telegram as parameter) or returns the saved
	 * telegram. The new telegram is built up from the members in this class (in
	 * this case contact A-D).
	 * 
	 * @return Created telegram
	 */
	public Telegram getTelegram() {
		int nrContacts = 0;
		int contact = 0;

		// create a new telegram?
		if (telegramCreation == true) {
			// check for set contacts
			if (this.getContactA() != BinaryState.UNKNOWN) nrContacts++;
			if (this.getContactB() != BinaryState.UNKNOWN) nrContacts++;
			if (this.getContactC() != BinaryState.UNKNOWN) nrContacts++;
			if (this.getContactD() != BinaryState.UNKNOWN) nrContacts++;

			// DB0-DB2 are empty
			telegram.setData0(0);
			telegram.setData1(0);
			telegram.setData2(0);

			// set type to "send"
			telegram.setXtelegram_type("6B");

			switch (nrContacts) {

			// no contact is pressed
			case 0:
				telegram.setStatus(0x20);
				telegram.setData3(0x00);
				break;

			// one contact pressed
			case 1:
				if (this.getContactA() == BinaryState.FALSE) telegram.setData3(0x01 << 5);
				if (this.getContactA() == BinaryState.TRUE) telegram.setData3(0x00 << 5);
				if (this.getContactB() == BinaryState.FALSE) telegram.setData3(0x03 << 5);
				if (this.getContactB() == BinaryState.TRUE) telegram.setData3(0x02 << 5);
				if (this.getContactC() == BinaryState.FALSE) telegram.setData3(0x05 << 5);
				if (this.getContactC() == BinaryState.TRUE) telegram.setData3(0x04 << 5);
				if (this.getContactD() == BinaryState.FALSE) telegram.setData3(0x07 << 5);
				if (this.getContactD() == BinaryState.TRUE) telegram.setData3(0x06 << 5);
				telegram.setStatus(0x30);
				telegram.setData3(telegram.getData3() | 0x10);
				break;

			// 2 pressed contacts
			case 2:
				if (this.getContactA() == BinaryState.FALSE) {
					telegram.setData3(0x01 << 5);
					contact = 1;
				}
				if (this.getContactA() == BinaryState.TRUE) {
					telegram.setData3(0x00 << 5);
					contact = 1;
				}
				if (this.getContactB() == BinaryState.FALSE) {
					telegram.setData3(0x03 << 5);
					contact = 2;
				}
				if (this.getContactB() == BinaryState.TRUE) {
					telegram.setData3(0x02 << 5);
					contact = 2;
				}
				if (this.getContactC() == BinaryState.FALSE) {
					telegram.setData3(0x05 << 5);
					contact = 3;
				}
				if (this.getContactC() == BinaryState.TRUE) {
					telegram.setData3(0x04 << 5);
					contact = 3;
				}
				if (this.getContactD() == BinaryState.FALSE) {
					telegram.setData3(0x07 << 5);
					contact = 4;
				}
				if (this.getContactD() == BinaryState.TRUE) {
					telegram.setData3(0x06 << 5);
					contact = 4;
				}

				if ((this.getContactA() == BinaryState.FALSE) && (contact != 1)) {
					telegram.setData3(0x01 << 1);
				}
				if ((this.getContactA() == BinaryState.TRUE) && (contact != 1)) {
					telegram.setData3(0x00 << 1);
				}
				if ((this.getContactB() == BinaryState.FALSE) && (contact != 2)) {
					telegram.setData3(0x03 << 1);
				}
				if ((this.getContactB() == BinaryState.TRUE) && (contact != 2)) {
					telegram.setData3(0x02 << 1);
				}
				if ((this.getContactC() == BinaryState.FALSE) && (contact != 3)) {
					telegram.setData3(0x05 << 1);
				}
				if ((this.getContactC() == BinaryState.TRUE) && (contact != 3)) {
					telegram.setData3(0x04 << 1);
				}
				if ((this.getContactD() == BinaryState.FALSE) && (contact != 4)) {
					telegram.setData3(0x07 << 1);
				}
				if ((this.getContactD() == BinaryState.TRUE) && (contact != 4)) {
					telegram.setData3(0x06 << 1);
				}

				telegram.setStatus(0x30);
				telegram.setData3(telegram.getData3() | 0x11);
				break;

			// more than 2 are not possible (yet)
			default:
				return null;
			}
		}
		telegram.setORG(0x05);
		return telegram;
	}

	public BinaryState getContactA() {
		return contactA;
	}

	public BinaryState getContactB() {
		return contactB;
	}

	public BinaryState getContactC() {
		return contactC;
	}

	public BinaryState getContactD() {
		return contactD;
	}

	public int getNrContactsChanged() {
		return nrContactsChanged;
	}

	public int getSize() {
		return size;
	}

	public BinaryState[] getContact() {
		return contact;
	}

	public void setContactA(BinaryState contactA) {
		this.contactA = contactA;
	}

	public void setContactB(BinaryState contactB) {
		this.contactB = contactB;
	}

	public void setContactC(BinaryState contactC) {
		this.contactC = contactC;
	}

	public void setContactD(BinaryState contactD) {
		this.contactD = contactD;
	}

	public void setContact(BinaryState[] contact) {
		this.contact = contact;
	}

	public void setSize(int size) {
		this.size = size;
	}

}
